package com.jkgh.test;

import com.jkgh.asin.StateMachine;
import com.jkgh.asin.buffers.DataConsumer;
import com.jkgh.remedium.RemediumConstants;
import com.jkgh.remedium.objects.RemediumDataObject;
import com.jkgh.remedium.objects.messages.RemediumInMessage;
import com.jkgh.remedium.objects.messages.RemediumOutMessage;
import com.jkgh.remedium.states.Future;
import com.jkgh.remedium.states.GatheringState;
import com.jkgh.remedium.tools.RemediumTools;

/**
 * The main server object.
 * 
 * @author RS2JGenerator
 *
 */
public class MainServerObject extends RemediumDataObject {

	/**
	 * A name of this server.
	 */
	public String name;
	
	/**
	 * All versions supported by this server.
	 */
	public int[] versions;
	
	/**
	 * A parent server object. May be null to indicate top level server object.
	 */
	public MainServerObject[] parents;

	/**
	 * A server-side controller for this server object.
	 */
	public MainServerController controller;
	
	/**
	 * Default constructor.
	 */
	public MainServerObject() {	
	}
	
	/**
	 * Setting constructor.
	 * 
	 * @param name A name of this server.
	 * @param versions All versions supported by this server.
	 * @param parents Mommy and daddy.
	 * @param controller A server-side controller for this server object.
	 */
	public MainServerObject(String name, int[] versions, MainServerObject[] parents, MainServerController controller) {
		this.name = name;
		this.versions = versions;
		this.parents = parents;
		this.controller = controller;
	}
	
	// --------------------------------------------------------------------------------
	
	@Override
	public void produceContent(DataConsumer consumer, RemediumOutMessage message) {
		RemediumTools.produceString(consumer, name);
		RemediumTools.produceIntArray(consumer, versions);
		RemediumTools.produceObjectArray(consumer, message, parents);
		message.produceObject(controller, consumer);
	}

	public static void consume(StateMachine machine, final RemediumInMessage message, final Future<MainServerObject> future) {
		machine.setCurrentState(new GatheringState(machine, 1) {
			
			@Override
			protected void onNeededBytesAvailable() {
				switch (machine.produceByte()) {

				case RemediumConstants.NULL:
					future.goWith(null);
					break;
					
				case RemediumConstants.HISTORY:
					future.goWith((MainServerObject) message.fromHistory(machine.produceInt()));
					break;
					
				case RemediumConstants.VALUE:
					consumeContent(machine, message, future);
					break;
				}
			}
		});
	}

	private static void consumeArray(final StateMachine machine, final RemediumInMessage message, final Future<MainServerObject[]> future) {
		machine.setCurrentState(new GatheringState(machine, 4) {
			
			@Override
			protected void onNeededBytesAvailable() {
				final int length = machine.produceInt();
				if (length == -1) {
					future.goWith(null);
				} else if (length == 0) {
					future.goWith(new MainServerObject[0]);
				} else {
					consume(machine, message, new Future<MainServerObject>() {

						private int i = 0;
						private MainServerObject[] result = new MainServerObject[length];
						
						@Override
						public void goWith(MainServerObject o) {
							result[i++] = o;
							if (i == length) {
								future.goWith(result);
							} else {
								consume(machine, message, this);
							}
						}
					});
				}
			}
		});
	}
	
	private static void consumeContent(final StateMachine machine, final RemediumInMessage message, final Future<MainServerObject> future) {
		final MainServerObject ret = new MainServerObject();
		message.toHistory(ret);
		RemediumTools.consumeString(machine, new Future<String>() {

			@Override
			public void goWith(String o) {
				ret.name = o;
				RemediumTools.consumeIntArray(machine, new Future<int[]>() {

					@Override
					public void goWith(int[] o) {
						ret.versions = o;
						MainServerObject.consumeArray(machine, message, new Future<MainServerObject[]>() {

							@Override
							public void goWith(MainServerObject[] o) {
								ret.parents = o;
								future.goWith(ret);
							}
						});
					}
					
				});
			}
		});
	}
}
